import os
import sys
import threading
import time  # 时间操作
import traceback
from datetime import datetime
import requests
import winsound
from bs4 import BeautifulSoup  #
from selenium import webdriver  # 浏览器
from selenium.common.exceptions import UnexpectedAlertPresentException, NoSuchElementException, \
    StaleElementReferenceException, ElementNotInteractableException, \
    ElementClickInterceptedException, NoSuchWindowException, InvalidArgumentException, WebDriverException, \
    TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
import socket

from PyNewVersion.AppWindow import CustomEnum, CustomThread

'''获取ip'''


def getLocalIP():
    socketC = None
    try:
        socketC = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        socketC.connect(('8.8.8.8', 80))
        ip = socketC.getsockname()[0]
    finally:
        socketC.close()
    return ip


'''自定义异常处理'''


def customExcept(method, exception, exceptMethod, limit):
    index = 0
    while True:
        try:
            method(True)
            return 1
        except exception as e:
            index = index + 1
            if index > limit:
                # traceback.print_exc()  # 打印异常
                break
            if exceptMethod == 0:
                return 0
            elif exceptMethod is None:
                raise e
            elif exceptMethod is not None:
                try:
                    exceptMethod(True)
                except Exception as e:
                    raise e
            continue
    pass


# 邮件地址url
emailBaseUrl = "http://182.92.68.170:8086/api/v3"
# 爬取的地址
automateUrl = "https://table-web.ybdoctor.com/order"


class MainAutomate:
    def __init__(self):
        self.userIp = "127.0.0.1"
        self.userName = "System"
        # 当前时间
        self.current_date = time.strftime('%Y-%m-%d', time.localtime(time.time()))
        self.current_time = time.strftime('%Y-%m-%d %H.%M.%S', time.localtime(time.time()))
        # 日志保存的位置
        self.basePath = None
        self.logsPath = None
        self.saveLogPath = None
        self.capturesPath = None
        # selenium webdriver
        self.drivers = {}
        # 是否启用提示音
        self.isOpenDong = True
        #是否启用过期时间监控线程
        self.isOpenExpiresTime = False
        self.cookies = None
        self.runMode = None
        self.childThreads = {}
        self.childThreadMathod = {}
        self.orderStatus = 0
        self.isHasOrder = False
        self.isRefresh = False
    '''######爬虫主方法#####'''

    def startAutomate(self, driver):
        # 等待进入刷单主页面 等待登录成功
        self.isRefreshPage(driver, None)
        # init user
        self.initUser(driver)
        self.DongDong()
        # try:
        #     self.sendMessage("测试测试！！",driver,2)
        # except Exception as e:
        #     traceback.print_exc()
        # return
        # init thread group
        self.initThreadGroup(driver)
        while True:
            try:
                startBtns = driver.find_elements(By.CLASS_NAME, "ant-btn")
                for startBtn in startBtns:
                    if startBtn.text is not None and str(startBtn.text).replace(" ", "") == '开始接诊':
                        driver.execute_script("arguments[0].click();", startBtn)
            except Exception as e:
                s = 1
            for threadName in self.childThreads.keys():
                thread = self.childThreads[threadName]
                if thread.exitcode == 1:
                    print(thread.exc_traceback)
                    if "WebDriverException" in thread.exc_traceback:
                        self.log("浏览器因为特殊原因关闭了，准备重开。")
                        raise WebDriverException("chrome not reachable")
                    if "NoSuchWindowException: Message: no such window: target window already closed" in thread.exc_traceback:
                        self.log("浏览器因为特殊原因关闭了，准备重开。")
                        raise NoSuchWindowException("no such window: target window already closed")
                    thread = CustomThread(self.childThreadMathod[threadName],driver)
                    thread.setName(threadName)
                    thread.start()
        pass
    '''判断是否进入订单池刷新页面'''
    def isRefreshPage(self, driver, code=None):
        try:
            # 等待弹框
            time.sleep(3)
            driverWait = WebDriverWait(driver, 120, 0.5)
            driverWait.until(lambda diver: diver.find_element(By.XPATH, "//*[text()='刷新']"))
        except UnexpectedAlertPresentException:
            customExcept(lambda diver: driver.execute_script("arguments[0].click();", driver.find_element(By.CLASS_NAME, 'ant-modal-close')), Exception, code, 5)
        pass

    '''判断是否进入工作台页面'''
    def isWorkPage(self, driver):
        # workBox___34wa9
        # 等待
        try:
            driverWait = WebDriverWait(driver, 10, 0.1)
            driverWait.until(lambda diver: diver.find_element(By.CLASS_NAME, "workBox___34wa9"))
        except (TimeoutException,UnexpectedAlertPresentException):
            return 0
        self.log("进入订单池")
        pass
    '''跳转到订单池'''
    def goRefreshPage(self,driver):
        customExcept(lambda diver: driver.find_elements(By.CLASS_NAME, "ant-menu-item")[0].click(), Exception
                     ,lambda diver: driver.find_elements(By.CLASS_NAME, "ant-menu-item")[0].click(), 5)
        self.isHasOrder = False
        self.orderStatus = 0
        self.isRefresh = True
        pass
    '''子线程组'''
    def initThreadGroup(self, driver):
        self.log("初始化子线程组 ...")
        threadName = "closeTipsWindowThread"
        closeTipsWindowThread = CustomThread(self.closeTipsWindow, driver)
        closeTipsWindowThread.name = (threadName)
        closeTipsWindowThread.start()
        self.childThreads[threadName] = closeTipsWindowThread
        self.childThreadMathod[threadName] = self.closeTipsWindow
        threadName = "refreshThread"
        refreshThread = CustomThread(self.refreshThread, driver)
        refreshThread.name  = (threadName)
        refreshThread.start()
        self.childThreads[threadName] = refreshThread
        self.childThreadMathod[threadName] = self.refreshThread
        threadName = "acceptOrderThread"
        acceptOrderThread = CustomThread(self.acceptOrderThread, driver)
        refreshThread.name = threadName
        acceptOrderThread.start()
        self.childThreads[threadName] = acceptOrderThread
        self.childThreadMathod[threadName] = self.acceptOrderThread
        threadName = "confirmOrderThread"
        confirmOrderThread = CustomThread(self.confirmOrderThread, driver)
        confirmOrderThread.name  = (threadName)
        confirmOrderThread.start()
        self.childThreads[threadName] = confirmOrderThread
        self.childThreadMathod[threadName] = self.confirmOrderThread
        threadName = "expiresTimeOrderThread"
        expiresTimeOrderThread = CustomThread(self.expiresTimeOrderThread, driver)
        expiresTimeOrderThread.setName(threadName)
        expiresTimeOrderThread.start()
        self.childThreads[threadName] = expiresTimeOrderThread
        self.childThreadMathod[threadName] = self.expiresTimeOrderThread
        self.log("子线程组初始化完成 ...")
        pass

    '''刷新子线程'''
    def refreshThread(self, driver):
        while True:
            try:
                if not self.isHasOrder:
                    flag = customExcept(lambda diver: driver.execute_script("arguments[0].click();", driver.find_element(By.XPATH,
                                 "//*[text()='刷新']/..")), Exception, 0, 5)
                    if flag == 0:
                        self.goRefreshPage(driver)
                    try:
                        refreshBtns = driver.find_elements(By.CLASS_NAME, "ant-btn")
                        for refreshBtn in refreshBtns:
                            if refreshBtn.text == '刷新':
                                driver.execute_script("arguments[0].click();", refreshBtn)
                    except Exception:
                        continue
            except UnexpectedAlertPresentException:
                continue
    '''接单子线程'''
    def acceptOrderThread(self, driver):
        while True:
            if not self.isHasOrder :
                flag = customExcept(lambda diver: driver.execute_script("arguments[0].click();", driver.find_element(By.XPATH,
                                                                                                                     "//*[text()='刷新']/..")), Exception, 0, 5)
                if flag == 0 :
                    continue
                try:
                    tbody = driver.find_element(By.CLASS_NAME, "ant-table-tbody")
                    if (tbody is not None) and (tbody.get_attribute("innerHTML") is not ''):
                        acceptBtn = driver.find_element(By.CLASS_NAME, "ant-btn-background-ghost")
                        driver.execute_script("arguments[0].click();", acceptBtn)
                        self.log("捕获了新处方单")
                        self.isHasOrder = True
                        flag = self.isWorkPage(driver)
                        if flag == 0:
                            self.log("接单失败！")
                            self.isHasOrder = False
                            continue
                        self.log("接单成功！")
                        # print(driver.page_source)
                except Exception:
                    continue
        pass

    '''确认订单子线程'''
    def confirmOrderThread(self, driver):
        index = 0
        while True:
            flag = self.isWorkPage(driver)
            if flag == 0:
                continue
            if self.isHasOrder:
                flag = customExcept(lambda diver: driver.execute_script("arguments[0].click();"
                         ,driver.find_element(By.CLASS_NAME, "preButton___2lN1Q")), Exception,0, 10) # preButton___2lN1Q
                if flag == 0:
                    index = index + 1
                    self.log("工作台没有处方单，计数3 < "+str(index))
                    if index < 4 :
                        driver.refresh()
                    if index > 3 :
                        # 返回跳转到订单池
                        index = 0
                        self.goRefreshPage(driver)
                    continue
                index = 0
                self.log("工作台有处方单，开始点击完成按钮。")
                ##开启获取处方过期时间的线程
                self.isOpenExpiresTime = True
                tipBtns = driver.find_elements(By.XPATH, ".//*[@class='ant-btn ant-btn-primary ant-btn-sm']")
                for tipBtn in tipBtns:
                    if (tipBtn.text.replace(' ', '')) == ("确 定".replace(' ', '')):
                        try:
                            driver.execute_script("arguments[0].click();",tipBtn)
                        except StaleElementReferenceException as e:
                            self.sendMessage("找不到接单完成按钮！！",driver,2,e)
                            continue
                        try:
                            #判断是否点击完成按钮成功，如果以下代码超时了则报错
                            driverWait = WebDriverWait(driver, 10, 0.1)
                            driverWait.until_not(lambda diver: diver.find_element(By.CLASS_NAME, "preButton___2lN1Q"))
                        except TimeoutException as e:
                            # 异常
                            self.sendMessage("点击完成接单按钮异常，无法通过！！！",driver,1,e)
                            continue
                        CustomEnum.orderCount = CustomEnum.orderCount + 1
                        self.log("===============处理了共"+CustomEnum.orderCount+"单=====================")
                        self.isOpenExpiresTime = False
                        self.goRefreshPage(driver)
            else:
                self.log("工作台没有处方单，跳转到订单池1。")
                self.goRefreshPage(driver)
        pass
    '''过期时间监控线程'''
    def expiresTimeOrderThread(self,driver):
        while True:
            if self.isOpenExpiresTime:
                # 获取倒计时html
                try:
                    driverWait = WebDriverWait(driver, 10, 0.1)
                    driverWait.until(lambda diver: diver.find_element(By.CLASS_NAME, "chatTime___1ZeLp"))
                except (TimeoutException,UnexpectedAlertPresentException):
                    continue
                chatTime =  driver.find_element(By.CLASS_NAME, "chatTime___1ZeLp")
                self.log(chatTime.text)
                expiresTimeStr = chatTime.find_element(By.CLASS_NAME, "ant-statistic-content-value")
                expiresTime = time.strptime(expiresTimeStr, '%H:%M:%S')
                #分钟
                if expiresTime.tm_min == 2:
                    self.sendMessage("有订单，还剩下最后的2分钟，如果没有处理，则会超时，请通知管理员！"+chatTime,driver)
                # 判断用法用量是否填充
                try:
                    driverWait = WebDriverWait(driver, 10, 0.1)
                    driverWait.until(lambda diver: diver.find_elements(By.CLASS_NAME, "ant-select-search__field"))
                except (TimeoutException,UnexpectedAlertPresentException):
                    continue
                try:
                    fields = driver.find_elements(By.CLASS_NAME, "ant-select-search__field")
                    for field in fields:
                        if field.get_attribute('value') is None or field.get_attribute("value") is '':
                            self.sendMessage("有订单，未填用法用量，如果没有处理，则会超时，请通知管理员！",driver)
                except Exception as e:
                    self.log("313行，获取用法用量报错啦")
                    self.log(e)
                    continue
    pass
    '''关闭提示弹窗子线程'''
    # 关闭弹窗
    def closeTipsWindow(self, driver):
        while True:
            if self.orderStatus == 1:
                self.log("订单已被抢了。")
                self.orderStatus = 0
            try:
                closeBtn = driver.find_element(By.XPATH,
                                               "//div[@class='ant-modal-footer']/div/button[@class='ant-btn ant-btn-primary']")
                driver.execute_script("arguments[0].click();", closeBtn)
            except Exception:
                self.orderStatus = 0
            try:
                closeBtns = driver.find_elements(By.XPATH, "//*[text()='确 定']")
                for btn in closeBtns:
                    driver.execute_script("arguments[0].click();", btn)
            except Exception:
                self.orderStatus = 0
            try:
                btns = driver.find_elements(By.CSS_SELECTOR, 'button[class="ant-btn ant-btn-primary"]')
                for btn in btns:
                    if "知道了" in btn.text:
                        driver.execute_script("arguments[0].click();", btn)
            except Exception:
                self.orderStatus = 0
            try:
                closeBtns = driver.find_elements(By.CLASS_NAME, 'ant-modal-close')
                for btn in closeBtns:
                    driver.execute_script("arguments[0].click();", btn)
            except Exception:
                self.orderStatus = 0
            try:
                closeBtn = driver.find_element(By.CSS_SELECTOR, 'div[class="ant-tooltip-inner"] > a')
                driver.execute_script("arguments[0].click();", closeBtn)
            except Exception:
                self.orderStatus = 0
            try:
                closeBtn = driver.find_element(By.CLASS_NAME, 'ant-notification-notice-close')
                driver.execute_script("arguments[0].click();", closeBtn)
            except Exception:
                self.orderStatus = 0
    '''提示音'''
    def DongDong(self):
        if self.isOpenDong:
            winsound.Beep(400, 900)
        pass
    '''浏览器截图'''
    def capture(self,driver):
        try:
            folder = os.path.exists(self.capturesPath)
            if not folder:
                os.makedirs(self.capturesPath)
            filePath = self.capturesPath+'/'+self.current_time+".png"
            print("开始截图！"+ filePath)
            flag =driver.get_screenshot_as_file(filePath)
            if flag:
                print("截图成功！")
                return filePath
            else:
                print("截图失败！")
        except Exception as msg:
            print("截图失败！")
            print(msg)
        pass
    '''# 发送短信'''
    def sendMessage(self, content, driver,level=3,exception=None):
        self.log("=================" + content + "===================")
        if exception:
            self.log(" exception >>>>>>>>{ " + exception)
        self.saveLogAndHtml(driver.page_source)
        # 浏览器截图
        capturePath = self.capture(driver)
        errorPath = self.saveLogPath+'/'+self.current_date+"-error.txt"
        content = ("登录名:" + str(self.userName) + ",IP地址" + str(self.userIp) + ",报错内容:" + content)
        ##### 上传
        files = {
            "file1":("log.txt", open(errorPath, "rb"),"image/png"),
            "file2":("capture.png",open(capturePath, "rb"), "image/png")
        }
        res = requests.post(
            emailBaseUrl + "/mailSettings/SetMailSettingOrEntryCodeFileUploadsNew?entryCode=0001&content=" + content + "&type=0",
            files=files)
        print("邮件服务器返回:"+res.text)
        self.log("发送短信成功")
        if level == 1:
            time.sleep(6)
        elif level == 2:
            time.sleep(12)
        else:
            time.sleep(16)
    #  上传html和日志
    def saveLogAndHtml(self, html):
        folder = os.path.exists(self.saveLogPath)
        if not folder:
            os.makedirs(self.saveLogPath)
        errorFile = open(self.saveLogPath+'/'+self.current_date+"-error.txt", "w", encoding="utf-8")
        errorFile.write("==========前100行日志 start===========")
        logFile = open(self.logsPath+'/'+self.current_date+"-log.txt", "r", encoding="utf-8")
        errorFile.write("\n")
        logs = logFile.readlines()[-100:]
        for log in logs:
            errorFile.write(log)
        errorFile.write("\n")
        errorFile.write("==========前60行日志 end===========")
        errorFile.write("\n")
        errorFile.write("\n")
        errorFile.write(str(html))
        errorFile.close()
    '''日志记录的方法'''
    def log(self, message):
        try:
            print(str(self.userName) + " say:" + str(message))
            folder = os.path.exists(self.logsPath)
            if not folder:
                os.makedirs(self.logsPath)
            log = open(self.logsPath + '/' + self.current_date + "-log.txt", "a", encoding="utf-8")
            log.write("==========打印的时间=" + str(datetime.now()) + "==========")
            log.write("\n")
            log.write(str(message))
            log.write("\n")
        except Exception:
            # self.sendMessage("保存日志报错操作，立即通知管理员,message = 'log void error!'", self.driver.page_source)
            traceback.print_exc()
        pass

    '''# 创建浏览器 并且 设置配置'''

    def newChrome(self, userDataPath, index):
        options = self.initChromeOptions(userDataPath)
        # init chrome
        self.initChrome(options, index)
        pass

    # init user
    def initUser(self, driver):
        self.userName = driver.find_element(By.XPATH,
                                            ".//*[@class='ant-avatar avatar___2IGyD ant-avatar-circle ant-avatar-image']/following-sibling::span").text
        self.userIp = getLocalIP()
        pass

    '''配置webdriver option # init chrome option'''

    def initChromeOptions(self, userDataPath):
        # init chrome option
        options = webdriver.ChromeOptions()
        options.add_argument('--disable-software-rasterizer')
        options.add_experimental_option("excludeSwitches", ['enable-automation', 'enable-logging'])
        options.add_argument('--ignore-certificate-errors')
        options.add_argument(r"user-data-dir=" + userDataPath)
        # 设置中文
        options.add_argument('lang=zh_CN.UTF-8')
        # 去掉受自动化测试软件提示
        options.add_experimental_option('useAutomationExtension', False)
        # 更换头部
        ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36'
        options.add_argument('user-agent="%s"' % ua)
        isExists = os.path.exists(userDataPath)
        if not isExists:
            os.makedirs(userDataPath)
        return options

    '''########运行/重启浏览器########'''

    def initChrome(self, options, index):
        driver = None
        if options is None:
            self.log("请先运行newConfig配置方法！")
            return
        self.log("正在启动浏览器 ...")
        while True:
            try:
                driver = webdriver.Chrome("./chromedriver.exe", options=options)
                self.drivers[index] = driver
                break
            except InvalidArgumentException:
                if index == 1000:
                    os.system('taskkill /F /IM chrome.exe')
                else:
                    self.drivers[index].quit()
                continue
        if self.runMode is CustomEnum.TEST and self.cookies is not None:
            driver.add_cookie(cookie_dict=self.cookies)
        driver.get(automateUrl)
        self.log("启动浏览器成功 ...")

